//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.transaction
{


	using TransactionManager = javax.transaction.TransactionManager;

	using XaDataSource = org.neo4j.kernel.impl.transaction.xaframework.XaDataSource;

///
/// <summary> * Can reads a XA data source configuration file and registers all the data
/// * sources defined there or be used to manually add XA data sources.
/// * <p>
/// * This module will create a instance of each <seealso cref="XaDataSource"/> once started
/// * and will close them once stopped.
/// *  </summary>
/// * <seealso cref= XaDataSourceManager </seealso>
/// 
	public class TxModule
	{
		private const string MODULE_NAME = "TxModule";

		private bool startIsOk = true;
		private string txLogDir = "var/tm";

		private readonly TransactionManager txManager;
		private readonly XaDataSourceManager xaDsManager;

		public TxModule(string txLogDir)
		{
			this.txLogDir = txLogDir;
			this.txManager = new TxManager(txLogDir);
			this.xaDsManager = new XaDataSourceManager();
		}

		public TxModule(bool readOnly)
		{
			if (readOnly)
			{
				this.txManager = new ReadOnlyTxManager();
				this.xaDsManager = new XaDataSourceManager();
			}
			else
			{
				throw new IllegalStateException("Read only must be set for this constructor");
			}
		}

		public virtual void init()
		{
		}

		public virtual void start()
		{
			if (!startIsOk)
			{
				return;
			}
			if (txManager is TxManager)
			{
				((TxManager)txManager).init(xaDsManager);
			}
			else
			{
				((ReadOnlyTxManager)txManager).init(xaDsManager);
			}
			startIsOk = false;
		}

		public virtual void reload()
		{
			Stop();
			Start();
		}

		public virtual void stop()
		{
			xaDsManager.unregisterAllDataSources();
			if (txManager is TxManager)
			{
				((TxManager)txManager).Stop();
			}
			else
			{
				((ReadOnlyTxManager)txManager).Stop();
			}
		}

		public virtual void destroy()
		{
		}

		public virtual string getModuleName()
		{
			return MODULE_NAME;
		}

///    
///     <summary> * Use this method to add data source that can participate in transactions
///     * if you don't want a data source configuration file.
///     *  </summary>
///     * <param name="name">
///     *            The data source name </param>
///     * <param name="className">
///     *            The (full) class name of class </param>
///     * <param name="resourceId">
///     *            The resource id identifying datasource </param>
///     * <param name="params">
///     *            The configuration map for the datasource </param>
///     * <exception cref="LifecycleException"> </exception>
///     
		public virtual XaDataSource registerDataSource<T1>(string dsName, string className, sbyte[] resourceId, Map<T1> @params)
		{
			XaDataSourceManager xaDsMgr = xaDsManager;
			string name = dsName.ToLower();
			if (xaDsMgr.hasDataSource(name))
			{
				throw new TransactionFailureException("Data source[" + name + "] has already been registered");
			}
			try
			{
				XaDataSource dataSource = xaDsMgr.create(className, @params);
				xaDsMgr.registerDataSource(name, dataSource, resourceId);
				return dataSource;
			}
			catch (Exception e)
			{
				throw new TransactionFailureException("Could not create data source [" + name + "], see nested exception for cause of error", e);
			}
		}

		public virtual XaDataSource registerDataSource<T1>(string dsName, string className, sbyte[] resourceId, Map<T1> @params, bool useExisting)
		{
			XaDataSourceManager xaDsMgr = xaDsManager;
			string name = dsName.ToLower();
			if (xaDsMgr.hasDataSource(name))
			{
				if (useExisting)
				{
					return xaDsMgr.getXaDataSource(name);
				}
				throw new TransactionFailureException("Data source[" + name + "] has already been registered");
			}
			try
			{
				XaDataSource dataSource = xaDsMgr.create(className, @params);
				xaDsMgr.registerDataSource(name, dataSource, resourceId);
				return dataSource;
			}
			catch (Exception e)
			{
				throw new TransactionFailureException("Could not create data source " + name + "[" + name + "]", e);
			}
		}

		public virtual string getTxLogDirectory()
		{
			return txLogDir;
		}

		public virtual TransactionManager getTxManager()
		{
			return txManager;
		}

		public virtual XaDataSourceManager getXaDataSourceManager()
		{
			return xaDsManager;
		}
	}
}